﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Blizzard.Contracts;
using Blizzard.Interceptors;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.Data;
using System.Reflection;
using Blizzard.Container;
using System.Runtime.Serialization;
using Blizzard.DAL;
using Blizzard.Mapper;

namespace Blizzard.Container
{
   


        public class BlizzardManager
        {
            public event EnumDataConvertorDelegate dataTypeMapper;
            
            /// <summary>
            /// fires when One Relation Object is handled by Blizzard
            /// handling one relation could mean Deletion or Insertion of
            /// associated objects. Simple Update of objects is not included
            /// </summary>
            public event Processing OnPersistingRelations;
            private IDAO _dataAccess;
            private IMapper _mapper;

            //=======================================<public>=====================================

            /// <summary>
            /// Resets all containers, all changes on objects will be lost and not persisted
            /// </summary>
            public void ResetAllContainers()
            {
                DBObjectContainer.ClearContainer();
                RelationDefinitionContainer.ClearContainer();
                RelationInstanceContainer.ClearContainer();
                NewObjectContainer.ClearContainer();
            }

            public BlizzardManager(IDAO dataAccess , IMapper mapper)
            {

                _dataAccess = dataAccess;
                _mapper = mapper;
                //_interceptor = new Interceptor(this);

            }


            /// <summary>
            /// Loads list of objects of given type from database. if whereClause is an emptystring
            /// then it loads all of objects from db (to use where clause please use the where key word too)
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="whereClause"></param>
            /// <returns></returns>
            public BList<T> Load<T>(string whereClause) where T : class, IDataObject, new()
            {
                BList<T> list = new BList<T>(null);
                string table = _mapper.GetTableName(typeof(T));

                DataTable dt = _dataAccess.GetDataTable(table, whereClause);
                list = Load<T>(dt);

                return list;
            }

            /// <summary>
            /// Gets all loaded IDataobjects that Blizzard has in its container
            /// </summary>
            /// <returns></returns>
            public BList<IDataObject> GetAllLoadedDataObjects() 
            {
                BList<IDataObject> list = new BList<IDataObject>(null);


                foreach (IDataObject obj in DBObjectContainer.GetAllDataObjects())
                {
                    list.Add(obj);
                }

                return list;
            }

            /// <summary>
            /// Gets all loaded objects of type T that Blizzard has in its container
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <returns></returns>
            public BList<T> GetAllLoadedDataObjects<T>()
            {
                BList<T> list = new BList<T>(null);

                foreach (IDataObject obj in DBObjectContainer.GetAllDataObjects())
                {
                    if (obj.GetType() == typeof(T))
                    {
                        list.Add((T)obj);
                    }
                }

                return list;
            }

            /// <summary>
            /// Gets all new IDataObjects that Blizzard has in its container
            /// </summary>
            /// <returns></returns>
            public BList<IDataObject> GetAllNewDataObjects()
            {
                BList<IDataObject> list = new BList<IDataObject>(null);


                foreach (IDataObject obj in NewObjectContainer.GetAllDataObjects())
                {
                    list.Add(obj);
                }

                return list;
            }

            /// <summary>
            /// Gets all new objects of type T that Blizzard has in its container
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <returns></returns>
            public BList<T> GetAllNewDataObjects<T>()
            {
                BList<T> list = new BList<T>(null);

                foreach (IDataObject obj in NewObjectContainer.GetAllDataObjects())
                {
                    if (obj.GetType() == typeof(T))
                    {
                        list.Add((T)obj);
                    }
                }

                return list;
            }

            /// <summary>
            /// Updates all IDataobjects in containers in to the database.
            /// Also it resets all the object containers.
            /// </summary>
            public void UpdateAndReset()
            {
                update();
                ResetAllContainers();
            }
            /// <summary>
            /// Updates all IDataobjects in containers in to the database
            /// </summary>
            public void Update()
            {
                update();
            }


            public T CreateNewObject<T>() where T : class, IDataObject, new()
            {

                T t = Instantiate<T>();
                NewObjectContainer.Add(t);

                return t;
            }

            public T Load<T>(int Id) where T : class, IDataObject, new()
            {
                T res = null;
                if (DBObjectContainer.HasTheObject(Id, typeof(T)))
                {
                    res = (T)DBObjectContainer.GetDataObject(Id, typeof(T));
                }
                else
                {
                    T t = Instantiate<T>();
                    res = LoadSimpleObject(t, Id);

                    if (res != null)
                    {

                        //RelationDefinition defaultRd = CreateDefaultRelationDefinition<T>();
                        //CreateDefaultRelationInstance(defaultRd, t);

                        DBObjectContainer.Add(res);
                    }
                }


                return res;
            }

            public void MarkForDeletion(IDataObject obj)
            {
                Flows.Flow_ObjectDeleteMarker(obj);
            }

            //=======================================</public>=====================================


            /// <summary>
            /// Manages RelationDefinition object(s) for this type (creats them and adds them 
            /// to the RelationDefinitionContainer)
            /// Instantiate a proxy of the input type and also it adds it to the container
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <returns></returns>
            private T Instantiate<T>() where T : class, IDataObject,  new()
            {

                T t = new T(); //CreateProxy<T>();
                t.Manager = this;
                ManageRelationDefinitions(typeof(T));
   
                return t;
                
            }


            private BList<T> Load<T>(DataTable dt) where T : class, IDataObject, new()
            {
                BList<T> res = new BList<T>(null);

                string primaryKeyField = _mapper.GetIdentityFieldName(typeof(T));
                primaryKeyField = primaryKeyField.Replace("[", "");
                primaryKeyField = primaryKeyField.Replace("]", "");


                foreach (DataRow row in dt.Rows)
                {
                    int id = (int)row[primaryKeyField];

                    if (DBObjectContainer.HasTheObject(id, typeof(T)))
                    {
                        res.Add((T)DBObjectContainer.GetDataObject(id, typeof(T)));
                    }
                    else
                    {

                        T Tobj = Instantiate<T>();
                        T singleLoadedObject = LoadSimpleObject(Tobj, row);
                        res.Add(singleLoadedObject);

                        if (res != null)
                        {
                            //release it later....................................................
                            //RelationDefinition defaultRd = CreateDefaultRelationDefinition<T>();
                            //CreateDefaultRelationInstance(defaultRd, t);
                            DBObjectContainer.Add(singleLoadedObject);
                        }
                    }
                }

                return res;
            }

            internal T LoadSimpleObject<T>(T obj, int Id) where T : class, IDataObject
            {
          
                string table = _mapper.GetTableName(typeof(T));
                string identityFieldName = _mapper.GetIdentityFieldName(typeof(T));
                DataTable dt = _dataAccess.GetDataTable(table,identityFieldName, Id);

                T resultObject = null;
                if (dt.Rows.Count != 0)
                {
   
                    DataRow row = dt.Rows[0];
                    resultObject = FillSimpleObject<T>(obj, row);

                }

               return resultObject;


            }
            internal T LoadSimpleObject<T>(T obj, DataRow row) where T : class, IDataObject
            {
    
                T resultObject = null;
                resultObject = FillSimpleObject<T>(obj, row);
                return resultObject;


            }


            /// <summary>
            /// Fills an object with data (only simple Properties will be filled, those properties that 
            /// implement IDataObjects or are Lists will remain null. 
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="obj"></param>
            /// <param name="row"></param>
            /// <returns></returns>
            private T FillSimpleObject<T>(T obj, DataRow row) where T : class, IDataObject
            {


                Type type = typeof(T);
                T resultObject = obj;


                //int x = 0;
                foreach (PropertyInfo pi in type.GetProperties())
                {
                    string fieldName = _mapper.GetFieldName(pi);
                    //because fields can be in form of [fieldname] (when they are reserved word
                    //or the field contains space

                    fieldName = fieldName.Replace("[", "");
                    fieldName = fieldName.Replace("]", "");

                    if (!string.IsNullOrEmpty(fieldName))
                    {

                        if (Helper.ImplementsIDataObjects(pi.PropertyType))
                        {
                            //when here we know that we have to do with a property that
                            //implements the IDataObjects interface, so it means that it is a reference to
                            //a parent object
                        }

                        else
                        {
 
                            if (row[fieldName] != DBNull.Value)
                            {
                                object propertyValue = row[fieldName];

                                if (pi.PropertyType.BaseType == typeof(Enum) && dataTypeMapper != null)
                                {
                                    propertyValue = dataTypeMapper(pi.PropertyType, propertyValue);
                                }
                                pi.SetValue(resultObject, propertyValue, null);
                            }
                        }
 
                    }
                }

         
                return resultObject;

            }

     /// <summary>
     /// Load associate objects from database and also relate them 
     /// (adds the relation to relationinstancecontainer)
     /// </summary>
     /// <typeparam name="TAssociate"></typeparam>
     /// <param name="mainObject"></param>
     /// <returns></returns>
            internal void LoadAssociates<TAssociate>(IDataObject mainObject) where TAssociate : class, IDataObject, new()
            {
  
                Type mainType = mainObject.GetType();
                Type AssociateType = typeof(TAssociate);

                RelationDefinition rd = RelationDefinitionContainer.GetRd(mainType, AssociateType);
              
                TypeRole mainTypeRole;
                TypeRole AssociateTypeRole;

                //finding out wich typeRole regards maintype en viseversa
                if (rd.TypeRole1.MyType == mainObject.GetType())
                {
                    mainTypeRole = rd.TypeRole1;
                    AssociateTypeRole = rd.TypeRole2;
                }
                else
                {
                    mainTypeRole = rd.TypeRole2;
                    AssociateTypeRole = rd.TypeRole1;
                }
                
  //======================================================

                DataTable dt = new DataTable();
           
                if (rd.Relation == RelationSort.ManyToMany)
                {//ManyToMany

                    string joinTable = rd.RelationTable;
                    string associateTable = _mapper.GetTableName(AssociateTypeRole.MyType);

                    string joinTableMainId = mainTypeRole.MyIdField;
                    string joinTableAssociateId = AssociateTypeRole.MyIdField;
                    //in the following line "Id" is fixed (it is the filed name of IDataObject interface
                    //and every DataObject should exactly have it as "Id"
                    string associatedId = _mapper.GetFieldName(AssociateTypeRole.MyType.GetProperty("Id"));

                    dt = _dataAccess.GetManyToManyAssociates(associateTable, joinTable, associatedId, joinTableAssociateId, joinTableMainId, mainObject.Id);
                }
                else //OneToMany
                {
                    string mainTable = _mapper.GetTableName(mainTypeRole.MyType);
                    
                    //in the following lines "Id" is fixed (it is the filed name of IDataObject interface
                    //and every DataObject should exactly have it as "Id"
                    string mainTableIdField = _mapper.GetFieldName(mainTypeRole.MyType.GetProperty("Id"));
                    
                    string associateTable = _mapper.GetTableName(AssociateTypeRole.MyType);
                    string associateTableIdField = _mapper.GetFieldName(AssociateTypeRole.MyType.GetProperty("Id"));

                    if (mainTypeRole.MyRole == Role.Child)
                    {
                        string associateIdFieldInMainTable = AssociateTypeRole.MyIdField;
                        dt = _dataAccess.GetParent(associateTable, mainTable, associateTableIdField, associateIdFieldInMainTable, mainTableIdField, mainObject.Id);

                    }
                    else if (mainTypeRole.MyRole == Role.Parent)
                    {
                        string mainTableIdFieldInAssociateTable = mainTypeRole.MyIdField;
                        dt = _dataAccess.GetChilderen(associateTable, mainTable, mainTableIdFieldInAssociateTable, mainTableIdField, mainObject.Id);

                    }

                }

             

                foreach (DataRow row in dt.Rows)
                {

                    //Please note that in this method we can not check first for
                    //existance of object and then go to database, because when we
                    //query the db we dont do it just for one object we do it in 
                    //a query that gets a set of datas from db.the only thing we can do
                    //is after we loaded the object from db we check if it is already
                    //in container (not itself but similar object with same combination of
                    // id-type) we will use the already existing copy (similar object).

                    TAssociate AssociateObj = Instantiate<TAssociate>();
                    FillSimpleObject<TAssociate>(AssociateObj, row);

                    if (DBObjectContainer.HasTheObject(AssociateObj.Id, typeof(TAssociate)))
                        {
                            IDataObject SimilarObjectInContainer = DBObjectContainer.GetDataObject(AssociateObj.Id, typeof(TAssociate));
                            RelationInstanceContainer.GetOrCreateRelationShip(mainObject, SimilarObjectInContainer);
                        }
                    else
                        {
                            DBObjectContainer.Add(AssociateObj);
                            RelationInstanceContainer.GetOrCreateRelationShip(mainObject, AssociateObj);
                        }
                    
                }

            }
            
            /// <summary>
            /// Creates RelationDefinitions for the given type, if they are not already in RelationDefinitionContainer, it adds it to it
            /// </summary>
            /// <param name="obj"></param>
            private void ManageRelationDefinitions(Type mainType)
            {

                string table = _mapper.GetTableName(mainType);


                // the following loop, finds out the relation the maniobj is in per property
                foreach (PropertyInfo pi in mainType.GetProperties())
                {


                    if (Helper.ImplementsIDataObjects(pi.PropertyType))
                    {//When here, we are dealing with a child(mainType) to parent relationship

                        Type AssociateType = pi.PropertyType;

                        if (!RelationDefinitionContainer.HasRd(mainType, AssociateType))
                        {
                            //creating TypeRole1 and TypeRole2 for this relation ship:
                            TypeRole typeRole1 = new TypeRole(mainType);
                            typeRole1.MyRole = Role.Child; // this is the role of maintype in this relationship 
                            typeRole1.MyIdField = ""; // as a child there is no field needed to persist the id of child
                            //in a relationship (the parent id will be persisted in the child table)

                            TypeRole typeRole2 = new TypeRole(AssociateType);
                            typeRole2.MyRole = Role.Parent;
                            typeRole2.MyIdField = _mapper.GetFieldName(pi);

                            RelationDefinition rd = new RelationDefinition(typeRole1, typeRole2);
                            rd.RelationTable = table;

                            if (_mapper.IsPartOfComposition(pi))
                            {
                                rd.Relation = RelationSort.OneToManyComposition;
                            }
                            else
                            {
                                rd.Relation = RelationSort.OneToMany;
                            }

                            RelationDefinitionContainer.Add(rd);
                        }
                    }
                    else if (Helper.IsAList(pi.PropertyType))
                    {

                        string joinTable = _mapper.GetJoinTableName(pi);

                        if (!String.IsNullOrEmpty(joinTable))
                        {//When here, we are dealing with a ManyToMany relationship

                            //remember when we are here pi regards to a BList property
                            //in the following line we find out the generic argument for the BList object
                            //this would be the associate type (the other type in relation)
                            Type AssociateType = Helper.GetTypeOfGenericArgument(pi.PropertyType);

                            if (!RelationDefinitionContainer.HasRd(mainType, AssociateType))
                            {

                                TypeRole typeRole1 = new TypeRole(mainType);
                                typeRole1.MyRole = Role.Associate; // this means many to many relation
                                typeRole1.MyIdField = _mapper.GetMyIdFieldName(pi);

                                TypeRole typeRole2 = new TypeRole(AssociateType);
                                typeRole2.MyRole = Role.Associate;

                                //finding out the MyIdField for the asociated type:
                                foreach (PropertyInfo pi_inAssociatedType in AssociateType.GetProperties())
                                {
                                    if (Helper.IsAList(pi_inAssociatedType.PropertyType))
                                    {
                                        typeRole2.MyIdField = _mapper.GetMyIdFieldName(pi_inAssociatedType);
                                    }
                                }

                                RelationDefinition rd = new RelationDefinition(typeRole1, typeRole2);
                                rd.RelationTable = joinTable;
                                rd.Relation = RelationSort.ManyToMany;

                                RelationDefinitionContainer.Add(rd);
                            }
                        }
                        else
                        {//When here, we are dealing with a parent(mainType) to child relationship


                            //remember when we are here pi regards to a BList property
                            //in the following line we find out the generic argument for the BList object
                            //this would be the associate type (the other type in relation)
                            Type AssociateType = Helper.GetTypeOfGenericArgument(pi.PropertyType);

                            if (!RelationDefinitionContainer.HasRd(mainType, AssociateType))
                            {

                                TypeRole typeRole1 = new TypeRole(mainType);
                                typeRole1.MyRole = Role.Parent;
                                bool IsPartOfCompostion = false;

                                foreach (PropertyInfo pi_inChildType in AssociateType.GetProperties())
                                {
                                    if (pi_inChildType.PropertyType == mainType)
                                    {
                                        typeRole1.MyIdField = _mapper.GetFieldName(pi_inChildType);
                                        IsPartOfCompostion = _mapper.IsPartOfComposition(pi_inChildType);
                                    }
                                }

                                TypeRole typeRole2 = new TypeRole(AssociateType);
                                typeRole2.MyRole = Role.Child;
                                typeRole2.MyIdField = "";// as a child there is no field needed to persist the id of child
                                //in a relationship (the parent id will be persisted in the child table)

                                RelationDefinition rd = new RelationDefinition(typeRole1, typeRole2);
                                rd.RelationTable = _mapper.GetTableName(AssociateType);


                                if (IsPartOfCompostion)
                                {
                                    rd.Relation = RelationSort.OneToManyComposition;
                                }
                                else
                                {
                                    rd.Relation = RelationSort.OneToMany;
                                }

                                RelationDefinitionContainer.Add(rd);
                            }

                        }




                    }

                }

                //

            }
       
            /// <summary>
            /// Update all objects in to database, also resets all containers
            /// </summary>
            private void update()
            {

                foreach (IDataObject newObject in NewObjectContainer.GetAllDataObjects())
                {

        
                    //Checking all the newObjects on their rol in their relationships
                    //when they have a child role in a compostion relationship. it should
                    //be checked whether the parent exists, if there is no parent for it
                    //then we have to do with an orphan, which naturally can not be persisted
                    //in data base, thats why it throws a CompositionException.

                    Type newObjectType = newObject.GetType();
                    
                    List<RelationDefinition> rdiees = RelationDefinitionContainer.GetAllRelations(newObjectType);

                    foreach (RelationDefinition rd in rdiees)
                    {
                        if (rd.Relation == RelationSort.OneToManyComposition)
                        {
                             Role roleOfMainObject = Helper.GetMyRoleInRelationShip(newObjectType, rd);
                            if (roleOfMainObject == Role.Child)
                            {

                                Type associatedType = Helper.GetAssociatedTypeInRelationShip(newObjectType, rd);
                                BList<IDataObject> associates = RelationInstanceContainer.GetAssociates(newObject, associatedType);

                                if (associates.Count == 0)
                                    throw new CompositionException();
                            }
                        }
                    }

                }


                 List<RelationInstance> allRelations = RelationInstanceContainer.GetAllRelations();
                int count = allRelations.Count; 
                int x = 0;
                foreach (RelationInstance ri in allRelations)
                {
                    if (this.OnPersistingRelations != null)
                        this.OnPersistingRelations(count, x++);

                    if (ri.Status == RelationStatus.Broken)
                    {
                        //if(ri.Status != RelationStatus.RemovedFromDataBase)    
                        Flow_RelationDeletion(ri);
                    
                    }
                }

                foreach (RelationInstance ri in allRelations)
                {
                    if (this.OnPersistingRelations != null)
                        this.OnPersistingRelations(count, x++);

                    if (ri.Status == RelationStatus.New)
                    {
                        Flow_RelationPersistance(ri);
                    }

                }

                foreach (WrappedData data in DBObjectContainer.GetAllWrappedData())
                {

                    if (data.IsMarkedForDeletion)
                    {
                        Flow_ObjectDeletion(data.innerDataObject);
                    }


                    if (data.IsChanged)
                    {
                        UpdateSimpleObject(data.innerDataObject);
                    }

                }

                //=======================================================================================
                //the following two loops removes the need for default relation ship
                List<IDataObject> InsertedObjects = new List<IDataObject>();
                foreach (IDataObject obj in NewObjectContainer.GetAllDataObjects())
                {
                    
                    int objID = InsertSimpleObject(obj);
                    obj.Id = objID;
                    InsertedObjects.Add(obj);
 
                }

                foreach (IDataObject InsertedObject in InsertedObjects)
                {
                   //The following remove and add operation, is to cover the issues of calling the Update()
                    //Instead of UpdateAndReset()
                    NewObjectContainer.RemoveFromContainer(InsertedObject);
                    DBObjectContainer.Add(InsertedObject); 
                }
           
            }

            private void Flow_RelationDeletion(RelationInstance relation)
            {

            

                if (relation.Rd.Relation == RelationSort.ManyToMany)
                {//R1
                    SimpleRelationDeletor(relation);
                    return;
                }
                else
                {//L1
                    SimpleRelationDeletor(relation);
                    RelationInstanceContainer.RemoveFromContainer(relation);

                    if (relation.Rd.Relation != RelationSort.OneToManyComposition)
                    {//L2
                        return;
                    }
                    else
                    {//L3
                        Type childType = relation.Rd.TypeRole1.MyRole == Role.Child ? relation.Rd.TypeRole1.MyType : relation.Rd.TypeRole2.MyType;
                        IDataObject child = relation.Obj1.GetType() == childType ? relation.Obj1 : relation.Obj2;

                        //the following check is necessary because it is possible that while the relation with the parent of this
                        //compositechild is broken, another relation is created (a parent switch is happened)
                        //In that case we dont need to delete the child because it will not be orphan in database
                        if (HasAllItsCompositeParents(child))
                        {//L4
                            return;
                        }
                        else 
                        {//L5
                            Flow_ObjectDeletion(child);
                        }
                    }
                }


            }

            private void Flow_ObjectDeletion(IDataObject obj)
            {

                List<RelationInstance> myRelations = RelationInstanceContainer.GetAllRelations(obj);
                foreach (RelationInstance relation in myRelations)
                {
                    //if(relation.Status != RelationStatus.RemovedFromDataBase )
                    Flow_RelationDeletion(relation);
                }

                DeleteSimpleObject(obj); 
            }

            /// <summary>
            /// The input relation should have status new
            /// </summary>
            /// <param name="relation"></param>
            private void Flow_RelationPersistance(RelationInstance relation)
            {
        

                if (relation.Rd.Relation == RelationSort.ManyToMany)
                {//R1

                    IDataObject obj1 = relation.Obj1;
                    IDataObject obj2 = relation.Obj2;

                    int obj1ID = 0;
                    int obj2ID = 0;

                    if (obj1.Id == 0)
                    {//R2
                       obj1ID = InsertSimpleObject(obj1);

                       //The following remove and add operation, is to cover the issues of calling the Update()
                       //Instead of UpdateAndReset()
                       NewObjectContainer.RemoveFromContainer(obj1); 
                       obj1.Id = obj1ID;
                       DBObjectContainer.Add(obj1); 
                    }
                 //R3

                    if (obj2.Id == 0)
                    {//R4
                        obj2ID = InsertSimpleObject(obj2);

                        //The following remove and add operation, is to cover the issues of calling the Update()
                        //Instead of UpdateAndReset()
                        NewObjectContainer.RemoveFromContainer(obj2);
                        obj2.Id = obj2ID;
                        DBObjectContainer.Add(obj2); 
                    }
                    //R5

                }
                else
                {//L1

                    int parentId = 0;
                    int childId = 0;

                    Type parentType = relation.Rd.TypeRole1.MyRole == Role.Parent ? relation.Rd.TypeRole1.MyType : relation.Rd.TypeRole2.MyType;
                    IDataObject parent = relation.Obj1.GetType() == parentType ? relation.Obj1 : relation.Obj2;


                    if (parent.Id == 0)
                    {//L2
                        parentId = InsertSimpleObject(parent);

                        //The following remove and add operation, is to cover the issues of calling the Update()
                        //Instead of UpdateAndReset()
                        NewObjectContainer.RemoveFromContainer(parent);
                        parent.Id = parentId;
                        DBObjectContainer.Add(parent); 

                    }
                    //L3

                    IDataObject child = relation.Obj1 == parent ? relation.Obj2 : relation.Obj1;

                    if (child.Id == 0)
                    {//L4
                        childId = InsertSimpleObject(child);
      
                        //The following remove and add operation, is to cover the issues of calling the Update()
                        //Instead of UpdateAndReset()
                        NewObjectContainer.RemoveFromContainer(child);
                        child.Id = childId;
                        DBObjectContainer.Add(child); 
                    }
                    //L5

                }

                

                SimpleRelationPersistor(relation);
                relation.Status = RelationStatus.None;
                string s = "xx";
                //RelationInstanceContainer.RemoveFromContainer(relation);

            }

            /// <summary>
            /// Persists many to many and one to many relations into database
            /// </summary>
            /// <param name="relation"></param>
            private void SimpleRelationPersistor(RelationInstance relation)
            {
                string joinTable = relation.Rd.RelationTable;
                string obj1_RelationPersistorField = relation.Rd.TypeRole1.MyIdField;
                string obj2_RelationPersistorField = relation.Rd.TypeRole2.MyIdField;

                if (relation.Rd.Relation == RelationSort.ManyToMany)
                {
                    
                    _dataAccess.JoinTableInserter(joinTable: joinTable, fieldNameOne: obj1_RelationPersistorField
                        , fieldNameTwo: obj2_RelationPersistorField, filedValueOne: relation.Obj1.Id, fieldValueTwo: relation.Obj2.Id);
                }
                else 
                {
                    string parent_RelationPersistorField = "";
                    int parentId = 0;
                    int childId = 0;
                    string childIdField = "";

                    if (!String.IsNullOrEmpty(obj1_RelationPersistorField))
                    {
                        parent_RelationPersistorField = obj1_RelationPersistorField;
                        parentId = relation.Obj1.Id;
                        childId = relation.Obj2.Id;
                        childIdField = _mapper.GetIdentityFieldName(relation.Rd.TypeRole2.MyType);
                    }
                    else
                    {
                        parent_RelationPersistorField = obj2_RelationPersistorField;
                        parentId = relation.Obj2.Id;
                        childId = relation.Obj1.Id;
                        childIdField = _mapper.GetIdentityFieldName(relation.Rd.TypeRole1.MyType);
                    }

                    //this method creates a  one to many relationship 
                    _dataAccess.SetParentId(childTable: joinTable, childIdField: childIdField, childId: childId, parentIdFieldInChildTable: parent_RelationPersistorField, parentIdValue: parentId);

                }

            }

            private void SimpleRelationDeletor(RelationInstance relation )
            {
                string joinTable = relation.Rd.RelationTable;
                string obj1_RelationPersistorField = relation.Rd.TypeRole1.MyIdField;
                string obj2_RelationPersistorField = relation.Rd.TypeRole2.MyIdField;


                //if(relation.Status == RelationStatus.RemovedFromDataBase)
                //    throw new ApplicationException("Relation has already been deleted from database!"); 

                if (relation.Rd.Relation == RelationSort.ManyToMany)
                {
                    //this method breaks the relationship
                    _dataAccess.JoinTableDeleter(joinTable, obj1_RelationPersistorField, obj2_RelationPersistorField, relation.Obj1.Id, relation.Obj2.Id);
                }
                else
                {

                    string parent_RelationPersistorField = "";
                    string childIdField = "";
                    int parentId = 0; 
                    int childId = 0;

                    if (!String.IsNullOrEmpty(obj1_RelationPersistorField))
                    {
                        parent_RelationPersistorField = obj1_RelationPersistorField;
                        parentId = relation.Obj1.Id;
                        childIdField = _mapper.GetIdentityFieldName(relation.Rd.TypeRole2.MyType);
                        childId = relation.Obj2.Id;
                    }
                    else
                    {
                        parent_RelationPersistorField = obj2_RelationPersistorField;
                        parentId = relation.Obj2.Id;
                         childIdField = _mapper.GetIdentityFieldName(relation.Rd.TypeRole1.MyType);
                        childId = relation.Obj1.Id;
                    }

                    //this method deletes the relationship by passing the parentIdValue as 0.
                    _dataAccess.SetParentId(childTable: joinTable,  childIdField: childIdField, childId: childId, parentIdFieldInChildTable: parent_RelationPersistorField, parentIdValue: 0 );
                }


                //relation.Status = RelationStatus.RemovedFromDataBase; //it seems that the  RelationStatus RemovedFromDataBase 
                //is not needed, but i still let it be, later i will clean it up
                
            }

            private void DeleteSimpleObject(IDataObject obj)
            {

                Type type = obj.GetType();
                string tableName = _mapper.GetTableName(type);
                string idField = _mapper.GetIdentityFieldName(type);

                
                _dataAccess.DeleteRecord(tableName, idField, obj.Id);
                DBObjectContainer.RemoveFromContainer(obj);
       
            }

            private void UpdateSimpleObject(IDataObject simpleObject)
            {
                Type type = simpleObject.GetType();
                string mainTable = _mapper.GetTableName(type);
                string identityField = _mapper.GetIdentityFieldName(type);

                Dictionary<string, object> fieldValuePairs = new Dictionary<string, object>();

                   
                foreach (PropertyInfo pi in type.GetProperties())
                {
                   string field = _mapper.GetFieldName(pi);

                    if (!string.IsNullOrEmpty(field))
                    {

                        if (!_mapper.IsIdentityColumn(pi))
                        {
                            object propertyValue = pi.GetValue(simpleObject, null);

                            if (propertyValue != null)
                            {

                                if (!Helper.ImplementsIDataObjects(pi.PropertyType))
                                {
                                    fieldValuePairs.Add(field, propertyValue);
                                }

                            }
                        }
                    }

                }

                _dataAccess.Update(mainTable, identityField, simpleObject.Id, fieldValuePairs);
  
            }

            /// <summary>
            /// inserts simple objects without any realtions (no parent id)
            /// </summary>
            /// <param name="simpleObject"></param>
            /// <param name="cnn"></param>
            /// <returns></returns>
            private int InsertSimpleObject(IDataObject simpleObject)
            {
                Type type = simpleObject.GetType();
                string mainTable = _mapper.GetTableName(type);

                Dictionary<string, object> fieldValuePairs = new Dictionary<string, object>();

                foreach (PropertyInfo pi in type.GetProperties())
                {

                    string field = _mapper.GetFieldName(pi);

                    if (!string.IsNullOrEmpty(field))
                    {

                        if (!_mapper.IsIdentityColumn(pi))
                        {
                                object propertyValue = pi.GetValue(simpleObject, null);

                                if (Helper.ImplementsIDataObjects(pi.PropertyType))
                                {
                                    //The value of a reference field (int) shouldnt be tested on null
                                    //because a reference field should always be 0 and not nulll
                                    //(at least at this stage, later this null will be set the a proper value
                                    //that is: the id of the parent).
                                    //for those objects who are involved in a Composite relationship with their
                                    //parents and the value of their parent field is null, we never come to this
                                    //stage, before the program flow reachs this place it will throw an exception
                                    //that is: CompositionException();
                                    fieldValuePairs.Add(field,(object)0);
                                }
                                else //Its ok for value to be null //if (propertyValue != null)
                                {
                                    if (pi.PropertyType == typeof(Enum))
                                    {
                                        fieldValuePairs.Add(field, propertyValue.ToString());
                                    }
                                    else
                                    {
                                        fieldValuePairs.Add(field, propertyValue);
                                    }
                                }


                            
                        }
                    }

                }

                _dataAccess.Insert(mainTable, fieldValuePairs);
                return _dataAccess.GetTheLastInsertedId(mainTable, _mapper.GetIdentityFieldName(type));
            }

 

            private bool HasAllItsCompositeParents(IDataObject obj)
            {
                List<RelationInstance> myRelations = RelationInstanceContainer.GetAllRelations(obj);

                List<RelationInstance> myCompositionRelations = new List<RelationInstance>();
                foreach (RelationInstance relation in myRelations)
                {

                    if (relation.Rd.Relation == RelationSort.OneToManyComposition)
                    {
                        myCompositionRelations.Add(relation);
                    }

                }

                List<RelationInstance> myNotBrokenCompositionRelations = new List<RelationInstance>();
                foreach (RelationInstance relation in myCompositionRelations)
                {

                    if (relation.Status != RelationStatus.Broken)
                    {
                        myNotBrokenCompositionRelations.Add(relation);
                    }

                }

                if (myNotBrokenCompositionRelations.Count == myCompositionRelations.Count)
                {
                    return true;
                }

                return false;
            }


        }

   
}
